package com.summer.bilibili.util;

import lombok.var;
import org.apache.tika.Tika;
import org.apache.tika.mime.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import java.util.regex.Pattern;

/**
 * 媒体类型检测工具类
 * <p>
 * 提供多种方式检测文件的媒体类型(MIME类型):
 * 1. 通过文件内容(魔数签名)
 * 2. 通过字节数组前缀
 * 3. 通过输入流
 * 4. 通过Base64编码字符串
 *
 * 核心实现基于Apache Tika库，提供准确的MIME类型检测
 *
 * @author summer
 */
public class DataUtils {
    // Tika实例（线程安全，静态初始化）
    private static final Tika TIKA_INSTANCE = new Tika ( );
    // 预编译的正则表达式，用于高效解析Base64前缀
    private static final Pattern BASE64_PATTERN = Pattern.compile ( "^data:([^;,]+)(;base64)?," );
    // Base64解码最大长度限制（4KB足够检测文件签名）
    private static final int BASE64_DECODE_LENGTH = 4096;

    // 防实例化
    private DataUtils () {
        throw new AssertionError (  );
    }

    /**
     * 统一媒体类型解析逻辑
     *
     * @param mediaTypeStr 媒体类型字符串
     * @return 解析后的MediaType对象，或null(如果输入无效)
     */
    private static MediaType parseMediaType ( String mediaTypeStr ) {
        return StringUtils.hasText ( mediaTypeStr ) ?
                MediaType.parse ( mediaTypeStr.trim ( ) ) : null;
    }

    /**
     * 通过文件检测媒体类型
     * <p>优先使用文件内容中的魔数签名(Magic Number)检测，
     * 如果失败则回退到文件扩展名检测
     *
     * @param file 目标文件
     * @return MediaType对象，如"image/png"，检测失败返回null
     */
    @Nullable
    public static MediaType getMediaType ( File file ) {
        try {
            return parseMediaType ( TIKA_INSTANCE.detect ( file ) );
        } catch (IOException e) {
            return null; // 文件读取异常时返回null
        }
    }

    /**
     * 通过字节数组前缀检测媒体类型
     * <p>适用于网络数据包等场景，仅需文件头部的部分字节
     *
     * @param prefix 文件的前缀字节(通常前4-16字节足够)
     * @return MediaType对象，检测失败返回null
     */
    @Nullable
    public static MediaType getMediaType ( byte[] prefix ) {
        try {
            return parseMediaType ( TIKA_INSTANCE.detect ( prefix ) );
        } catch (IllegalStateException e) {
            return null;
        }
    }

    /**
     * 通过输入流检测媒体类型
     * <p><b>注意：</b>此方法会消耗流的头部数据，可能导致流位置变化
     *
     * @param is 输入流
     * @return MediaType对象，检测失败返回null
     */
    @Nullable
    public static MediaType getMediaType ( InputStream is ) {
        try {
            return parseMediaType ( TIKA_INSTANCE.detect ( is ) );
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 通过Base64字符串检测媒体类型
     * <p>双重检测机制:
     * 1. 优先解析Base64前缀(如"data:image/png;base64")
     * 2. 如果前缀不存在，解码部分数据通过内容检测
     *
     * @param base64Text Base64编码的字符串
     * @return MediaType对象，检测失败返回null
     */
    @Nullable
    public static MediaType getMediaType(String base64Text) {
        // 空值防御
        if (!StringUtils.hasText(base64Text)) return null;

        // 方案1：正则解析前缀（高性能路径）
        var matcher = BASE64_PATTERN.matcher(base64Text);
        if (matcher.find()) {
            return parseMediaType(matcher.group(1));
        }

        // 方案2：内容检测（兜底路径）
        try {
            // 计算需要解码的长度（不超过最大限制）
            int decodeLength = Math.min(base64Text.length(), BASE64_DECODE_LENGTH);
            // 解码前N字节
            byte[] decoded = Base64.getDecoder().decode(base64Text.substring(0, decodeLength));
            return getMediaType(decoded); // 通过字节检测
        } catch (Exception e) {
            return null; // 解码失败或检测异常
        }
    }
}
